package network

import (
	"context"
	"net"

	"gitee.com/doraemon1996/bunny/goroutine"
	"gitee.com/doraemon1996/bunny/log"
	"gitee.com/doraemon1996/bunny/uuid"
)

// UDP监听器
type UDPListener struct {
	listener

	rawLn *net.UDPConn // 底层UDP连接
}

// 新建UDP监听器
func NewUDPListener(ctx context.Context, network, address string) (ln *UDPListener, err Error) {
	addr, underlyingErr := net.ResolveUDPAddr(network, address)
	if underlyingErr != nil {
		err = &networkError{ErrorType_Underlying, underlyingErr}
		return
	}

	rawLn, underlyingErr := net.ListenUDP(network, addr)
	if underlyingErr != nil {
		err = &networkError{ErrorType_Underlying, underlyingErr}
		return
	}

	ln = new(UDPListener)
	ln.id = uuid.GenUint64()
	ln.network = network
	ln.address = address
	ln.rawLn = rawLn
	ln.ctl.Init(ctx, nil)

	return
}

// 获取监听器类型
func (ln *UDPListener) Type() ListenerType {
	return Listener_UDP
}

// 获取底层监听器 UDP不存在
func (ln *UDPListener) RawListener() any {
	return ln.rawLn
}

// 开始运行监听器的循环逻辑
func (ln *UDPListener) Start(f OnConnect) {
	ln.ctl.Start(func() {
		ln.ctl.Add(1)
		goroutine.Submit(func() {
			defer ln.ctl.Done()
			ln.acceptLoop(f)
		})
	})
}

// 停止并关闭监听器
func (ln *UDPListener) Stop() {
	ln.ctl.Stop(func() {
		log.Infof("%v is about to exit", ln)

		// 由于 rawLn 是一个 UDPConn,为了保证监听器与连接非强制绑定
		// 所以 rawLn 的实际关闭,需要调用对应的 UDPConn.Close()
		// 但是,当 UDPConn 使用的 Context 与 UDPListener 是同一个时, UDPConn 将会被同步关闭
		// if ln.rawLn != nil {
		// 	if err := ln.rawLn.Close(); err != nil {
		// 		log.Errorf("UDPListener close underlying listener catch error: %v", err)
		// 	}
		// }

		ln.ctl.Wait()
	})
}

// 端口监听循环
func (ln *UDPListener) acceptLoop(f OnConnect) {
	defer func() {
		if val := recover(); val != nil {
			log.Errorf("UDPListener underlying listener accept catch error: %v", val)
		}
	}()

	f(NewUDPConn(ln, ln.rawLn, true))
}
